#include "pthread.h"
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "signal.h"


#define BUFF_SIZE   5

typedef struct _repo{
    unsigned buf[BUFF_SIZE];
    pthread_mutex_t lock;
    pthread_cond_t notempty;
    pthread_cond_t notfull;
    int read_pos, write_pos;
}repo_t;

repo_t repo;

void init(repo_t *repo)
{
    pthread_mutex_init(&repo->lock, NULL);
    pthread_cond_init(&repo->notempty, NULL);
    pthread_cond_init(&repo->notfull, NULL);

    repo->read_pos = 0;
    repo->write_pos = 0;
}

void destroy(repo_t *repo)
{
    pthread_mutex_destroy(&repo->lock);
    pthread_cond_destroy(&repo->notempty);
    pthread_cond_destroy(&repo->notfull);

    repo->read_pos = 0;
    repo->write_pos = 0;
}


void put(repo_t *repo, unsigned data)
{
    pthread_mutex_lock(&repo->lock);

    if((repo->write_pos + 1) % BUFF_SIZE == repo->read_pos){
        printf("producer wait for not full\n");
        pthread_cond_wait(&repo->notfull, &repo->lock);
    }

    repo->buf[repo->write_pos] = data;
    repo->write_pos++;

    if(repo->write_pos >= BUFF_SIZE){
        repo->write_pos = 0;
    }

    pthread_cond_signal(&repo->notempty);
    pthread_mutex_unlock(&repo->lock);
}

unsigned get(repo_t *repo)
{
    unsigned data;
    
    pthread_mutex_lock(&repo->lock);

    if(repo->read_pos == repo->write_pos){
        printf("consumer waite for not empty\n");
        pthread_cond_wait(&repo->notempty, &repo->lock);
    }

    data = repo->buf[repo->read_pos];
    repo->read_pos++;

    if(repo->read_pos >= BUFF_SIZE){
        repo->read_pos = 0;
    }

    pthread_cond_signal(&repo->notfull);
    pthread_mutex_unlock(&repo->lock);

    return data;
}


void *producer(void *arg)
{
    unsigned idx;
    
    for(idx = 1; idx <= 50; idx++)
    {
        sleep(1);
        printf("put data %d into repo.\n", idx);
        put(&repo, idx);
    }

    printf("producer stopper.\n");

    return NULL;
}


void *consumer(void *arg)
{
    unsigned idx;
    unsigned data;

    for(idx = 0; idx < 50; idx++)
    {
        sleep(2);
        data = get(&repo);
        printf("get data %d from repo.\n", data);
    }

    printf("consumer stopped\n");

    return NULL;
}

int main()
{
    pthread_t tid1, tid2;
    void *retval;

    init(&repo);

    pthread_create(&tid1, NULL, producer, 0);
    pthread_create(&tid2, NULL, consumer, 0);

    pthread_join(tid1, &retval);
    pthread_join(tid2, &retval);    

    destroy(&repo);

    return 0;
}
